﻿#include "Chapter4inflatable.h"
#include <vector>
#include <array>
#include <iomanip>
#include <iostream>
std::tm timeStruct;
using namespace std;

//这种写法不常用
/*struct 
{
    int x;
    int y;
}position;*/

//4.4 结构介绍
//基础结构体
void Struct()
{
    /*timeStruct.tm_year = 2024 - 1900; // 年份，从1900年开始
    timeStruct.tm_mon =  12 - 1;       // 月份，从0开始计数
    timeStruct.tm_mday = 0 - 31;           // 月份中的日期
    timeStruct.tm_hour = 0 - 24;          // 小时
    timeStruct.tm_min = 0 - 59;            // 分钟
    timeStruct.tm_sec = 0 - 59;            // 秒*/
    Chapter4inflatable guest = {
        "Glorious Gloria",
        1.88,
        29.99,
       tm{
           18,
           11,
           14,
           4,
           12,
           2024,
           337,
           4,
           0,    
           },
       };

  
    cout<<"guest name: "<<guest.name<<'\n';
    cout<<"guest volume : "<<guest.volume<<'\n';
    cout<<"guest price : "<<guest.price<<'\n';
    /*cout<<"guest time : "<<guest.timeStruct.tm_year<<"/"<<guest.timeStruct.tm_mon<<"/"<<guest.timeStruct.tm_mday<<char(32);
    cout<<guest.timeStruct.tm_hour<<":"<<guest.timeStruct.tm_min<<":"<<guest.timeStruct.tm_sec<<'\n';*/
    Chapter4inflatable perks{
        "perks",
        10,
        10.99,
    };
    cout<<perks.name<<":"<<perks.price<<'\n';

    //另外一种使用格式,本书不使用
    /*struct 
    {
        int x;
        int y;
    }myPosition = {10,20};
    cout<<"x: "<<myPosition.x<<'\n';
    cout<<"y: "<<myPosition.y<<'\n';*/
    
};
//数组类型的结构体
void StructArray()
{
    Chapter4inflatable guest[] = {
        {"Bambi",0.5,21.99},
        {"perks",0.5,11.11},  
    };
    for(int i = 0; i < sizeof(guest)/sizeof(guest[0]); i++)
    cout<<guest[i].name<<":"<<guest[i].price<<'\n';
}
//结构中的位字段,2^n -1 ,超出以后显示0,固定范围
void Struct_register()
{
    struct torgle_register
    {
        unsigned int price :4;
        unsigned int volume:1;
        bool goodIn :1;
        bool goodTorgle :1;
    };
    

    
    torgle_register torgle={15,1,true,false};
        
    /*torgle.price = 15;//0~15
    torgle.volume = 1;//0 或者1
    torgle.goodIn = true;
    torgle.goodTorgle = false;*/
    
    printf("price:%u\n",torgle.price);
    printf("volume:%u\n",torgle.volume);
    cout<<"goodIn:"<<torgle.goodIn<<'\n';
    cout<<"goodTorgle:"<<torgle.goodTorgle<<'\n';
}

//4.5
//1共用体 :节省内存：由于共用体的所有成员共享同一块内存空间
//2 类型转换：共用体可以用来在不同的数据表示之间进行转换，而不需要显式的类型转换。例如，你可以将一个整数值以整数形式存储，然后以浮点数形式读取，这实际上是在解释同一块内存的不同方式。
//3 数据打包：共用体可以用来创建复杂的数据结构，其中某些数据字段可能有多种可能的格式。
//4 位字段操作：共用体可以与位字段（bit fields）结合使用，允许程序员对单个位进行操作，这在处理硬件寄存器或需要紧凑数据表示的场合非常有用。
void Struct_Union()
{
    union one4all
    {
        unsigned int int_val:4;
        long  long_val;
        double double_val;
    };
    //可以使用one4all变量来存储int,long,double 条件在不同的时间进行
    one4all pail;
    one4all pail2;
    pail.int_val = 15;
    pail2.int_val = 2;
    cout<<pail.int_val<<'\n';
    cout<<pail2.int_val<<'\n';
    pail.double_val = 1.38;
    cout<<pail.double_val<<'\n';
    struct persion
    {
        string name;
        unsigned int type:1;
        union id
        {
            char int_val[20];
            long long long_val;
        }presionId;
    };
 
    persion ff2 = {};
    ff2.name = "zyf";
    ff2.type = 1;
    ff2.presionId.long_val = 110227198310180119LL;
    persion ff ={};
    ff.name = "zyf";
    ff.type = 1;
    //共用体中没有string类型。我只能使用char数组。然后使用strcpy_s函数
    strcpy_s(ff.presionId.int_val, "abcd");

    if (ff.type==1){cout<<"man:"<<ff.name<<" ,id:"<<ff.presionId.int_val<<'\n';}
    else cout<<"felma:"<<ff.name<<" ,id:"<<ff.presionId.int_val<<'\n';
    if (ff2.type==1){cout<<"man:"<<ff2.name<<" ,id:"<<ff2.presionId.long_val<<'\n';}
    else cout<<"felma:"<<ff2.name<<" ,id:"<<ff2.presionId.long_val<<'\n';
}

//4.6enumeration定义符号常量,可以代替const
void myeEnum(){
    myEnumH h = three;
    h = myEnumH(2);//h= h(2)是错误的.
    std::cout << h<< '\n'; // 打印枚举变量的整数值
    enum spectrum {red,orange,yellow,green,blue,violet,indigo,ultraviolet};
    spectrum sp;
    sp = spectrum(3);//是一个类型转换表达式，它将整数 3 转换为 spectrum 类型的值。这是合法的 sp=sp(3)不合法
    sp = green;
    //enum一般搭配Switch case 使用
    switch (sp)
    {
        case red:cout<<"red"<<'\n';break;
        case orange:cout<<"orange"<<'\n';break;
        case yellow:cout<<"yellow"<<'\n';break;
        case green:cout<<"green"<<'\n';break;
        case violet:cout<<"violet"<<'\n';break;
        case indigo:cout<<"indigo"<<'\n';break;
        case ultraviolet:cout<<"ultraviolet"<<'\n';break;
        default:cout<<"red"<<'\n';break;
    }
}

//4.7指针和存储空间 第三章说的 数据必须跟踪的三个属性:1 信息存储在哪里，2存储值是多少，3存储类型是什么
void addresTest()
{
    int i = 100;
    long j = 200;
    int k= 100;
    long l = 200;
    cout<<i<<" "<<j<<" "<<k<<" "<<l<<"\n";
    cout<<&i<<" "<<&j<<" "<<&k<<" "<<&l<<"\n";
    cout<<"address test"<<'\n';
}

//oop 的指针,运行时监测,和编译时监测.
//c++也可以和c一样写运行时监测的代码
//比如使用new 请求正确数量的内存以及使用指针来跟踪新分配的内存位置
//每一个指针都需要有一个*
void pointTest(){
    
    int updates =6;
    //*p_updates 表示存储在改地址的int值
    //关于风格int* p_updates(c++)和*p_updates(c语言).
    //int* p_updates = &updates;
    int* p_updates;
    //值的地址,指针和地址要一起使用
    p_updates = &updates;
    
    cout<<"Values: updates = "<<updates;
    cout<<",*p_update = "<<*p_updates<<'\n';
    
    cout<<"Addresses: &updates = "<<&updates;
    cout<<", p_update = "<<p_updates<<'\n';
    //updates,p_updates 是硬币的一体两面，修改一个影响另外一个
    //updates有一个值，使用&获得地址。

    *p_updates = *p_updates+1;
    //p_updates 是地址,通过*获得值,可以说*p_updates是int,p_updates是指针(地址)
    updates = updates-1;
    cout<<"Now, update = "<<*p_updates<<'\n';

    /*指针的危险,没有初始化可能导致不可预测风险*/
    long ff ;
    long* fellow=&ff;
    //可以直接写地址
    //fellow = (long *)0xb800000;
    
    cout<<"fellow = "<<*fellow<<'\n';
    
    //使用new分配内存，c语言中可以使用malloc()分配内存C++也可以用,但有更好的方式new运算符
    int* pn=new int;
    //这个方式和上面的方式比较fellow =&ff,都是将int变量的地址分配给了指针
    //new 这种方式有一个问题,只能用指针去称呼变量,无法用ff 去称呼.
    //使用new的通常的格式:typeName * pointer_name = new typeName;
    //堆栈的概念,静态变量和值一般存储在堆stock.指针通过new指向的值,存储在栈heap中,栈是连续的进出自动释放内存,不有碎片,速度比堆更快,堆会有碎片需要手动释放内存
    int stone = 1001;
    int* p_stone =new int;
    *p_stone = 1001;
    cout<<"p_stone = "<<*p_stone<<'\n';
    cout<<"stone = "<<stone<<'\n';
    cout<<"*p_stone lenght= "<<sizeof(*p_stone)<<'\n';
    double *pd = new double;
    *pd = 10000001.1;
    cout<<fixed<<setprecision(2)<<"pd"<<*pd+*p_stone<<'\n';
    cout<<"*pd lenght= "<<sizeof(*pd)<<'\n';
    cout<<"stone lenght= "<<sizeof(stone)<<'\n';
    //只能用delete释放new的内存,一定要配对使用new 和delete.空指针使用delete是安全的
    delete p_stone;
    //1重复使用delete 地址释放两次.会错误. 2搭配new使用
    /*delete p_stone;*/
    delete pd;
    //会报错,释放了指针,cout<<"11111111111p_stone = "<<*p_stone<<'\n';
}

//用new创建动态数组
void new_Arrag()
{

    int arg[] = {1,3,4,5};
    //格式 type_name * pointer_name = new type_name [num_elements]
    int *pt= new int[4];
    //指针pt 会指向第一个元素pt[0],*pt返回的就是第一个元素的值
    pt[0] = arg[0];
    pt[1] = arg[1];
    pt[2] = arg[2];
    pt[3] = arg[3];
    
    cout<<*pt<<'\n';
    delete []pt;
    //delete arg;delete只能搭配new使用    
}

void dynamicArray()
{
    double * p3 =new double [3];
    p3[0] = 1.1;
    p3[1] = 2.2;
    p3[2] = 3.3;
    cout<<"p3[1] is :"<<p3[1]<<'\n';
    p3=p3+1;//移动指针到下一个位置
    cout<<"p3[1] is :"<<p3[0]<<'\n';
   
    cout<<"p3[1] is :"<<p3[1]<<'\n';
    p3=p3-1;//不还原指针delete会报错。。
    delete []p3;//如果你不恢复 p3 指针到原始位置并尝试释放它，你会得到一个运行时错误，
    
}

//指针加一等于增加一个字节数,int 16位.double 32位.boolean1位?
void pointerMath()
{
    double wages[4] = {1000.0,2000.0,3000.0,4000.0};
    cout<<"wages are :"<<wages<<'\n';
    short stacks[3] = {1,2,5};
    cout<<"stacks are :"<<stacks[0]<<'\n';
    
    //使用数组名 初始化指针式常用的。eages=&wages[0]=第一个元素地址
    //wages是数组的名字，它代表数组的首地址，即第一个元素的地址(这是一个特殊的写法)。wages作为数组名，它本身就是一个指向数组第一个元素的指针
    //&wages[0]是数组第一个元素的地址，它和wages是等价的。它本身就是一个指向数组第一个元素的指针
    double * pw = wages;
    short * ps = &stacks[0];
   
    cout<<"pw = "<<pw<<" ,*pw = "<<*pw<<'\n';
    cout<<"ps = "<<ps<<" ,*ps = "<<*ps<<'\n';
    //(ps+1)指针的位置会往后移动8位(double类型),移动两位(short类型).(ps+N)和stacks[n]等价
    cout<<"ps  sizeof = "<<*(ps+1)<<" ,*(stacks+1) = "<<*(stacks+1)<<'\n';
    cout<<"wages sizeof : "<<sizeof(wages)<<",pw sizeof : "<<sizeof(pw)<<'\n';
    double a=1.22;
    cout<<"a sizeof= "<<sizeof(a)<<'\n';
    int coats[10];
    *(coats +4) =14;
    cout<<"*(coats +4) :"<<coats[4]<<'\n';
}

void pointerChar()
{
    char c1[10] = "rose\0 1";
    //数组名是第一个字符串地址,cout会继续打印后面的字符直到遇到\0
    cout<<c1<<" is red "<<'\n';
    char c[20] = "bear";
    //const 指针,他不能被修改,用bird访问字符串,但是不能修改
    const char * bird = "wren";
    cout<<bird<<" is ren"<<'\n';
    //数组复制给指针,对于cout 使用数组名和指针名.是一样的,他们都是字符串的地址
    char * p ;
    p = c;
    /*cout<<"p : "<<p<<'\n';
    cout<<"输入c : ";cin>>c;
    //如果输入*p，指针的第一位会被存储到c的第一位
    cout<<"输入*p : ";cin>>p;
    cout<<"c : "<<c<<'\n';
    cout<<"p : "<<p<<'\n';
    
    cout<<"(int)c :"<<(int*)c<<'\n';
    cout<<"(int)*p:"<<(int*)p<<'\n';
    cout<<"strlen(c) : "<<strlen(c)<<'\n';*/
    
    char c2[20] = "bear";
    p =new char[strlen(c2)+1];
    //通过strncpy_s 和new 将获得两个独立的副本.strcpy被弃用
    //加上一个空字符的位置，避免了越界访问导致的堆损坏问题。如果不加入+1.会导致报错
    strncpy_s(p,strlen(c2) + 1,c2,strlen(c2));
    //对于cout 使用数组名和指针名.是一样的,他们都是字符串的地址
    cout<<"c2 : "<<c2<<'\n';
    cout<<"p : "<<p<<'\n';
    cout<<"(int)c2 :"<<(int*)c2<<'\n';
    cout<<"(int*)p:"<<(int*)p<<'\n';
    // int*将一个元素换成int类型的指针 
    //(void*)p 和 (int*)p 都会打印出相同的地址，但是推荐使用 (void*)p 来打印地址。
    cout<<"(void*)p:"<<(void*)p<<'\n';
    delete [] p;
    
}
//指针存储构造图
void pointerStruct()
{
    Chapter4inflatable * ps = new Chapter4inflatable;
    cout<<"Enter name of inflatable item: "<<'\n';
    //尝试读取19个字符
    cin.get(ps->name,20);
    cout<<"Enter volume in cubic feet"<<'\n';
    cin>>(*ps).volume;
    cout<<"Enter price : $ "<<'\n';
    cin>>ps->price;
    cout<<"name : "<<(*ps).name<<'\n';
    cout<<"volume : "<<ps->volume<<'\n';
    cout<<"price : "<<ps->price<<'\n';
    //构造体的定义读取
    Chapter4inflatable p{
    "面包",3,10};
    cout<<"name:"<<p.name<<'\n';
    cout<<"volume : "<<p.volume<<'\n';
    cout<<"price : "<<p.price<<'\n';
    delete ps;
}


char * getName()
{
    char temp[80];
    cout<<"Enter last name: ";
    cin>>temp;
    char *pn = new char[strlen(temp)+1];
    strcpy_s(pn,strlen(temp)+1,temp);
    return pn;
}
//自动存储，静态存储，和动态存储
//temp数组,仅在代码块中使用.自动存储.代码块执行完,自动释放,存储在栈中,lifo后进先出
//temp自动变量.实际上自动变量是一个局部变量
//static ,静态存储使用次关键字.另外一种方式是函数外定义,比如头文件定义π
//new和delete 元素安抚提供了一种比动态变量和静态变量更灵活的方式.他们管理一个内存池,C++中称为自由存储空间(free store)和堆heap
//避免内存泄露 同时使用new和delete. 
void pointerDelete()
{
    char *name;
    name = getName();
    cout<<name<<" at : "<<(void *)name<<'\n';
    delete [] name;

    name= getName();
    cout<<name<<" at : "<<(void *)name<<'\n';
    delete [] name;
}

void comboType()
{
    struct antractica_year_end
    {
        int year;
        
    };
    antractica_year_end s01,s02,s03;
    s01.year = 1998;
    antractica_year_end * pa = &s02;
    pa->year = 1999;
    antractica_year_end trio[3];
    trio[0].year = 2003;
    //数组的名字直接代表指针,指向第[0]个元素
    (trio+1)->year = 2004;
    //这种初始化方式在 C 语言中非常常见，尤其是在你需要处理多个结构体或类的实例，并且需要通过指针数组来访问
    const antractica_year_end * arp[3]={&s01,&s02,&s03};
    cout<<arp[1]->year<<'\n';
    //const antractica_year_end **ppa =arp;
    //当你使用数组名 arp 而不是它的元素时，它会被视为指向数组第一个元素的指针，即 const antractica_year_end**。
    auto ppa=arp;
  
    //双指针用名称获取ppa,不写**ppa.
    //*ppa记录的是arp[0]记录地址的值的地址.
    cout<<"*ppa : "<<*ppa<<'\n';
    cout<<"&s01 : "<<&s01<<'\n';
    cout<<"s01.year  : "<<&s01.year<<'\n';
    cout<<"(&s01)->year  : "<<(&s01)->year <<'\n';
    cout<<"(*ppa)->year  : "<<(*ppa)->year <<'\n';
    cout<<"(*(ppa+1))->year : "<<(*(ppa+1))->year <<'\n';
    cout<<"(void*)arp : "<<(void*)arp<<'\n';
    cout<<"arp : "<<arp<<'\n';
    cout<<"ppa : "<<ppa<<'\n';
    
    cout<<"&arp[0] :"<<arp[0]->year<<'\n';
}

//动态数组vector 和固定长度的数组array.都是容器数组.可以使用size()获取长度
//普通的数组 没有size()方法.只能使用sizeof(a)/sizeof(a[0])
void arrayIsAnther()
{
    vector<int> v= {1,2,3,4};
    //vector<int>是一个动态数组,不是简单的内存块,需要使用内部函数获得指针.data();
 
    // 添加一个新元素.1分配一块更大的内存空间。
    //2将现有元素从旧内存块复制到新内存块。
    //3释放旧内存块。
    v.push_back(11);
    //第三个元素后插入
    v.insert(v.begin()+1,6);
    int * p = v.data();
    for (int i = 0; i <  v.size(); ++i)
        cout<<* (p+i)<<'\n';

    //array初始化就固定数组的大小.不能改变.但是可以修改里面的值
    array<int,5> ai;
    array<double,4> ad={1.2,2.1,3.43,4.3};
    ad[1]=2.3;
    double * adp=ad.data();
    for (int i = 0; i <  ad.size(); ++i)
        cout<<"ad : "<<*(adp+i)<<'\n';
    //sizeof打印出占用字节数.一个int占用4个.
    cout<<sizeof(ai);
    cout<<ad.at(3);
    
    //会报错.因为数组是从0开始的,指针定义到数组外
    //ad[-2]=.5;
    //ad[100]=5;
    //cout<< ad[100];
}
//数组 结构 指针 是三种复合类型.
void finalTest()
{
    //1声明下述数据
    /*char actor[30];
    short betsie[100];
    float chuck[13];
    long double disea[64];*/
    //2使用模版类array 而不是数组来完成1
    /*array<char,30> actor;
    array<short,100> betsie;
    array<float,13> chuck;
    array<long double,64> disea;*/
    //3声明一个包含5个元素的int数组,并将他初始化为前5个正奇数
    int five[]={1,3,5,7,9};
    //4边写一条语句,将问题3中数组的第一个元素和最后一个元素的和赋值给变量even
    int array_size = sizeof(five) / sizeof(five[0]); // 获取数组长度
    int last_element = five[array_size - 1]; // 获取最后一个元素
    int even= five[0]+last_element;cout<<even<<'\n';
    //5编写一条语句显式float数组ideas中的第二个元素的值
    float ideas[]={1.0,2.0,3.0,4.0,5.0,6.0};cout<<ideas[1]<<'\n';
    //6声明一个char的数组,并将其初始化为字符串"cheeseburger"
    char c1[20]="cheeseburger";
    //7声明一个string对象,初始化为字符串"Waldorf Salad"
    string s1 = "Waldorf Salad";
    //8声明一个描述鱼结构的声明.结构中应当暴扣的品种,重量(整数盎司)和长度(包括小数)"
    struct finsh
    {
        string name;
        int aoci;
        double length;
    };
    //9声明一个问题八中定义的结构的变量,并对其初始化.
    finsh f{"caoyu",1,03};
    /*f.name = "caoyu";
    f.aoci = 1;
    f.length = 0.3;*/
    //10用enum定义一个名为Response的类型，它包含Yes、No和Maybe等枚举量，其中Yes的值为1，No为0，Maybe为2。
    enum Response{No=0,Yes=1,Maybe=3};
    Response response = Yes;
    cout<<"response : "<<response<<'\n';
    cout<<"response Yes: "<<Yes<<'\n';
   
    //11声假设ted是一个double变量，请声明一个指向ted的指针，并使用该指针来显示ted的值。
    double ted=1.55;
    double * pt= &ted;
    cout<<*pt<<'\n';
    //12假设treacle是一个包含10个元素的float数组，请声明一个指向treacle的第一个元素的指针，并使用该指针来显示数组的第一个元素和最后一个元素。
    double reacle[10]={1.0,2.0,3.0,4.0,5.0,6.0,7.1,8.1,9.1,10.1};
    double * rp=reacle;
    cout<<*rp<<'\n';
    cout<<*(rp+9)<<'\n';
    //13编写一段代码，要求用户输入一个正整数，然后创建一个动态的int数组，其中包含的元素数目等于用户输入的值。首先使用new来完成这项任务，再使用vector对象来完成这项任务。
    //int * p =new int;p=v;这样写会内存泄露;指针从新指向v.new的int不能通过delete释放.
    /*int size;
    cout<<"输入一个正整数:";
    cin>>size;
    int* dynamicArray = new int[size];
    for(int i=0;i<size;i++){
        dynamicArray[i]=i;
        cout<<dynamicArray[i]<<'\n';
    }
    delete [] dynamicArray;
    
    vector<int> v(size);
    for(int i=0;i<size;i++)
    {
        //v.push_back(i);这是末尾添加.不是从0开始赋值
        v[i] = i;
        cout<<v[i]<<'\n';
    }
    cout<<"v.size: "<<v.size()<<'\n';*/
    //14下面的代码是否有效？如果有效，它将打印出什么结果？答:可能会报错.也可能显式一个int类型的指针地址.(int*)指向一个地址int类型.
    cout <<"(int *)+str :"<< (int *)"Home of the jolly bytes"<<'\n';
    //15编写一段代码，给问题8中描述的结构动态分配内存，再读取该结构的成员的值。
    finsh * pf =&f;
    cout<<pf->name<<'\n';
    cout<<pf->aoci<<'\n';
    cout<<pf->length<<'\n';
    //16 4.6程序中getline 和cin混合使用的问题是什么? 
    /*char address[100];
    cin.getline(address,80);
    // 忽略掉getline留下的换行符
    //std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    cin>>address;*/
    //答:读取getline后会在末尾输入换行符(回车).cin会读取回车直接结束(跳过输入).如果避免这个情况需要中间加一个忽略换行
    //17声明一个vector对象和一个array对象，它们都包含10个string对象。指出所需的头文件，但不使用using。使用const来指定要包含的string对象数。
    //答:使用头文件#include<vector> 和<array>
    const string str1 = "Home of the jolly bytes";
    const string str2 = "Home of the jolly bytes";//...
    vector<string> v1 = {str1,str2};
    array<string,10> arg1 = {str1,str2};
    
}
/*
int main(){
    //Struct();
    //StructArray();
    //Struct_register();
    //Struct_Union();
    /*myEnumH h = one; // 将枚举值 one 赋值给枚举变量 h
    std::cout << static_cast<int>(h) << '\n'; // 打印枚举变量的整数值#1#
    //myeEnum();
    //addresTest();
    //pointTest();
    //new_Arrag();
    //dynamicArray();
    //pointerMath();
    //pointerChar() ;
    //pointerStruct();
    //pointerDelete();
    //comboType();
    //arrayIsAnther();
    finalTest();
    return 0; 
};
*/
